package com.openarena.model;

import com.openarena.model.dao.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Logger;
import javax.persistence.*;

@Entity
@Table(name = "games")
public class Game implements java.io.Serializable, Comparable<Game> {

    private static final Logger logger = Logger.getLogger(Game.class.getCanonicalName());
    private Integer id;
    private Integer duration;
    private String winner;
    private Date playedDate;
    private Set<GameStat> stats = new HashSet<GameStat>(0);
    private transient Map<Integer, GameStat> statsMap;
    private transient List<Player> mvps;

    public Game() {
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "duration")
    public Integer getDuration() {
        return this.duration;
    }

    public void setDuration(Integer duration) {
        this.duration = duration;
    }

    @Column(name = "winner")
    public String getWinner() {
        return this.winner;
    }

    public void setWinner(String winner) {
        this.winner = winner;
    }

    @Column(name = "played_date")
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    public Date getPlayedDate() {
        return this.playedDate;
    }

    public void setPlayedDate(Date playedDate) {
        this.playedDate = playedDate;
    }

    public String formattedDate() {
        Date date = this.getPlayedDate();
        if (date == null) {
            return "No hay datos de fecha para este juego";
        } else {
            return new SimpleDateFormat("HH:mm dd-MM-yyyy").format(date);
        }
    }

    public String formattedLength() {
        int minutosJuego = this.getDuration() / 60;
        int segundosJuego = this.getDuration() % 60;
        String tiempoJuego = minutosJuego + ":" + ((segundosJuego >= 10) ? segundosJuego : "0" + segundosJuego);

        return tiempoJuego;
    }

    @Override
    public int compareTo(Game t) {
        return this.getPlayedDate().compareTo(t.getPlayedDate());
    }

    public boolean played(Player player) {
        return getStatMap().containsKey(player.getId());
    }

    public Integer points(Player player) {
        return played(player) ? getStatMap().get(player.getId()).getPoints() : 0;
    }

    public Integer kills(Player player) {
        return played(player) ? getStatMap().get(player.getId()).getKills() : 0;
    }

    public Integer deaths(Player player) {
        return played(player) ? getStatMap().get(player.getId()).getDeaths() : 0;
    }

    public boolean won(Player player) {
        return played(player) ? getStatMap().get(player.getId()).getWinner() == 1 : false;
    }

    public boolean mvp(Player player) {
        return getMVPs().contains(player);
    }

    @OneToMany(mappedBy = "game", fetch = FetchType.LAZY)
    public Set<GameStat> getStats() {
        return stats;
    }

    public void setStats(Set<GameStat> stats) {
        this.stats = stats;
    }

    @Transient
    private synchronized Map<Integer, GameStat> getStatMap() {
        if (statsMap == null) {
            statsMap = new HashMap<Integer, GameStat>(stats.size());
            for (GameStat stat : stats) {
                statsMap.put(stat.getPlayerId(), stat);
            }
        }

        return statsMap;
    }

    @Transient
    public synchronized List<Player> getMVPs() {
        if (mvps == null) {
            mvps = new ArrayList<Player>();

            int maxPoints = 0;
            PlayerDAO playerDAO = new PlayerDAO();

            for (GameStat stat : stats) {
                int points = stat.getPoints();
                if (points > maxPoints) {
                    maxPoints = points;
                    mvps.clear();
                    mvps.add(playerDAO.getPlayerById(stat.getPlayerId()));
                } else if (points == maxPoints) {
                    mvps.add(playerDAO.getPlayerById(stat.getPlayerId()));
                }
            }
        }

        return mvps;
    }

    @Transient
    public List<Player> getPlayers() {
        List<Player> players = new ArrayList<Player>();

        PlayerDAO playerDAO = new PlayerDAO();
        for (GameStat stat : stats) {
            players.add(playerDAO.getPlayerById(stat.getPlayerId()));
        }

        Collections.sort(players, new Comparator<Player>() {

            @Override
            public int compare(Player t, Player t1) {
                return t.getName().compareToIgnoreCase(t1.getName());
            }
        });

        return players;
    }

    @Transient
    public List<GamePoint> getGamePoints() {
        return new GamePointsDAO().getGamePointsByGame(this);
    }

    public boolean hasPreviousGame() {
        return new GameDAO().existGameBeforeDate(this.playedDate);
    }

    public boolean hasNextGame() {
        return new GameDAO().existGameAfterDate(this.playedDate);
    }

    @Transient
    public Game getPreviousGame() {
        return new GameDAO().getPreviousGame(this);
    }

    @Transient
    public Game getNextGame() {
        return new GameDAO().getNextGame(this);
    }

    @Transient
    public List<FlagCapture> getCaptures() {
        List<FlagCapture> captures = new ArrayList<FlagCapture>();

        PlayerDAO playerDAO = new PlayerDAO();

        List<GameFlagEvent> flagEvents = new GameFlagEventDAO().getGameFlagCaptures(this);
        FlagCapture lastCapture = null;
        for (GameFlagEvent gameFlagEvent : flagEvents) {

            if (lastCapture == null || !lastCapture.getGameTime().equals(gameFlagEvent.getGameTime())) {
                lastCapture = new FlagCapture();

                lastCapture.setGameTime(gameFlagEvent.getGameTime());
                lastCapture.setTeam(gameFlagEvent.getTeam());
                captures.add(lastCapture);
            }

            lastCapture.getPlayers().add(playerDAO.getPlayerById(gameFlagEvent.getPlayerId()));
        }

        return captures;
    }

    @Transient
    public List<Player> getRedTeam() {
        List<Player> players = new ArrayList<Player>();

        PlayerDAO playerDAO = new PlayerDAO();
        GameTeamDAO gameTeamDAO = new GameTeamDAO();
        for (GameStat stat : stats) {
            Player player = playerDAO.getPlayerById(stat.getPlayerId());
            if (gameTeamDAO.getTeamByGameAndPlayer(this, player).getTeam().equals(GameTeamEnum.RED.toString())) {
                players.add(playerDAO.getPlayerById(stat.getPlayerId()));
            }
        }

        Collections.sort(players, new Comparator<Player>() {

            @Override
            public int compare(Player t, Player t1) {
                return getStatMap().get(t1.getId()).getPoints().compareTo(getStatMap().get(t.getId()).getPoints());
            }
        });

        return players;
    }

    @Transient
    public List<Player> getBlueTeam() {
        List<Player> players = new ArrayList<Player>();

        PlayerDAO playerDAO = new PlayerDAO();
        GameTeamDAO gameTeamDAO = new GameTeamDAO();
        for (GameStat stat : stats) {
            Player player = playerDAO.getPlayerById(stat.getPlayerId());
            if (gameTeamDAO.getTeamByGameAndPlayer(this, player).getTeam().equals(GameTeamEnum.BLUE.toString())) {
                players.add(playerDAO.getPlayerById(stat.getPlayerId()));
            }
        }

        Collections.sort(players, new Comparator<Player>() {

            @Override
            public int compare(Player t, Player t1) {
                return getStatMap().get(t1.getId()).getPoints().compareTo(getStatMap().get(t.getId()).getPoints());
            }
        });

        return players;
    }

    @Transient
    public List<GameComment> getComments() {
        return new GameCommentDAO().getGameComments(this);
    }

    public int kills(Player killer, Player victim) {
        return new GameKillsDAO().getCountOfGameKillsByGameAndKillerAndVictim(this, killer,victim);
    }
}
